#include "drawlib.h"
#include "iostream"
#include <cstdlib>

using namespace std;

void swap(int &val1, int &val2) // inline?
{
    int temp = val1;
    val1 = val2;
    val2 = temp;
}

// put a pixel only if coordinates are within image
inline void plot_gray(GrayPixel **ppPixels, int x, int y, int width, int height, int grayval) 
{
    if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
        ppPixels[y][x] = grayval;
    }
}

// put a pixel only if coordinates are within image
inline void plot_rgb(RGBPixel **ppPixels, int x, int y, int width, int height,
                     RGBPixel color) {
    if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
        ppPixels[y][x] = color;
    }
}

void line_gray(PamImage &im, int x1, int y1, int x2, int y2, int grayval) {
    int width = im.getWidth();
    int height = im.getHeight();
    int im_type = im.getImageType();
    if (!(im_type == GRAY_IMAGE) && !(im_type == BW_IMAGE))
    {
        std::cerr << "Error: line_gray not applicable to image type " << im_type << std::endl;
        throw 1;
    }
    GrayPixel **ppPixels = im.getGrayPixels();
    
    // adapted from http://en.wikipedia.org/wiki/Bresenham's_line_algorithm 2007-07-18

    bool steep = (abs(y2 - y1) > abs(x2 - x1));
    if (steep)
    {
        swap(x1, y1);
        swap(x2, y2);
    }
    if (x1 > x2)
    {
        swap(x1, x2);
        swap(y1, y2);
    }
    int deltax = x2 - x1;
    int deltay = abs(y2 - y1);
    int error = -deltax / 2;
    int ystep;
    int y = y1;
    if (y1 < y2)
    {
        ystep = 1;
    }
    else
    {
        ystep = -1;
    }
    for (int x = x1; x <= x2; ++x)
    {
        if (steep)
        {
            plot_gray(ppPixels, y, x, width, height, grayval);
        }
        else
        {
            plot_gray(ppPixels, x, y, width, height, grayval);
        }
        error += deltay;
        if (error > 0)
        {
            y += ystep;
            error -= deltax;
        }   
    }
}

void line_rgb(PamImage &im, int x1, int y1, int x2, int y2, int r, int g, int b) {
    // shameless copy of line_gray, adapted for color
    int width = im.getWidth();
    int height = im.getHeight();
    int im_type = im.getImageType();
    if (im_type != RGB_IMAGE) {
        std::cerr << "Error: line_rgb not applicable to image type " << im_type << std::endl;
        throw 1;
    }
    RGBPixel **ppPixels = im.getRGBPixels();
    RGBPixel color;
    color.r = r;
    color.g = g;
    color.b = b;
    color.m = 0; // maybe not?
    
    // adapted from http://en.wikipedia.org/wiki/Bresenham's_line_algorithm 2007-07-18

    bool steep = (abs(y2 - y1) > abs(x2 - x1));
    if (steep) {
        swap(x1, y1);
        swap(x2, y2);
    }
    if (x1 > x2) {
        swap(x1, x2);
        swap(y1, y2);
    }
    int deltax = x2 - x1;
    int deltay = abs(y2 - y1);
    int error = -deltax / 2;
    int ystep;
    int y = y1;
    if (y1 < y2) {
        ystep = 1;
    }
    else {
        ystep = -1;
    }
    for (int x = x1; x <= x2; ++x) {
        if (steep) {
            plot_rgb(ppPixels, y, x, width, height, color);
        } else {
            plot_rgb(ppPixels, x, y, width, height, color);
        }
        error += deltay;
        if (error > 0) {
            y += ystep;
            error -= deltax;
        }   
    }
}

void block_gray(PamImage &im, int x1, int y1, int x2, int y2, int grayval) {
    int width = im.getWidth();
    int height = im.getHeight();
    int im_type = im.getImageType();
    if (!(im_type == GRAY_IMAGE) && !(im_type == BW_IMAGE))
    {
        std::cerr << "Error: line_gray not applicable to image type " << im_type << std::endl;
        throw 1;
    }
    if (x1 < 0) x1 = 0;
    if (x2 < 0) x2 = 0;
    if (y1 < 0) y1 = 0;
    if (y2 < 0) y2 = 0;
    if (x1 >= width) x1 = width - 1;
    if (x2 >= width) x2 = width - 1;
    if (y1 >= height) y1 = height - 1;
    if (y2 >= height) y2 = height - 1;
    
    GrayPixel **ppPixels = im.getGrayPixels();
    for (int y = y1; y <= y2; ++y) {
        for (int x = x1; x <= x2; ++x) {
            ppPixels[y][x] = grayval;
        }
    }
}
